package v1

import (
	"errors"
	"fmt"
	"math/rand"
	"regexp"
	"strconv"
	"sync"
	"time"
	"zpay/app/models/order"
	"zpay/app/requests"
	"zpay/pkg/config"
	"zpay/pkg/logger"
	"zpay/pkg/response"

	"github.com/gin-gonic/gin"
	"github.com/smartwalle/alipay/v3"
	"go.uber.org/zap"
)

type OrderController struct {
	BaseAPIController
}

// 生成日期为基础的唯一订单号
// 时间微秒之后加入3位随机数
// 示例：20230606092322910546731
func GenerateOrderNo() string {
	date := time.Now().Format("20060102030405")
	micro := time.Now().Format(".000000")
	r := rand.Intn(1000)
	code := fmt.Sprintf("%s%s%03d", date, micro[1:], r)
	return code
}

var once = sync.Once{}
var client *alipay.Client

// 通过once对象确保instance只被初始化一次
func initAlipayClient() {
	once.Do(func() {
		// 只会被调用一次
		// 初始化 alipay client
		var err error
		if client, err = alipay.New(config.Get("alipay.appid"), config.Get("alipay.private_key"), true); err != nil {
			// 初始化创建失败，直接退出程序
			logger.Fatal("Error Occurred", zap.Error(err))
		}
		// 加载证书
		if err = client.LoadAliPayPublicKey(config.Get("alipay.public_key")); err != nil {
			// 加载证书失败，直接退出程序
			logger.Fatal("Error Occurred", zap.Error(err))
		}
	})
}

// Notify 支付宝回调通知
func (oct *OrderController) Notify(c *gin.Context) {

	// 初始化client
	initAlipayClient()
	// 解析回调请求
	if err := c.Request.ParseForm(); err != nil {
		logger.Error("Alipay Notify Parse Form Error", zap.Error(err))
		response.Abort500(c, "Decode notification request error!")
		return
	}
	var notification, err = client.DecodeNotification(c.Request.Form)
	if err != nil {
		logger.Error("Alipay Notify Error:", zap.Error(err))
		response.Abort500(c, "Decode notification request error!")
		return
	}
	logger.Info("Alipay Notify:", zap.String("notify_id", notification.NotifyId))

	// 收到通知，更新订单状态
	order_no := notification.OutTradeNo
	orderModel := order.GetByOrderNo(order_no)
	// 订单不存在
	if orderModel.ID == 0 {
		logger.Error("Alipay Notify Error:", zap.String("order_not_exists", order_no))
		response.Abort404(c, "Order not exists!")
		return
	}
	// 订单已支付
	if orderModel.IsPaid {
		logger.Warn("Alipay Notify Warn:", zap.String("order_has_been_paid", order_no))
		response.Success(c)
		return
	}
	// 更新订单信息
	trade_status := string(notification.TradeStatus)
	is_paid := (trade_status == "TRADE_SUCCESS" || trade_status == "TRADE_FINISHED")
	logger.Info("Alipay Notify Info:", zap.Strings("order_status", []string{order_no, trade_status}))

	// 更新数据库订单记录
	orderModel.IsPaid = is_paid
	orderModel.Status = trade_status
	err = orderModel.Save()
	if err != nil {
		logger.Error("Save Model Error:", zap.Error(err))
		response.Abort500(c, "Internal server error, please contact the administrator~")
	} else {
		// 向支付宝发送接收通知，不然会重复收到通知
		client.ACKNotification(c.Writer)
	}
}

// Create 创建订单
func (oct *OrderController) Create(c *gin.Context) {

	// 验证表单
	request := requests.CreateOrderRequest{}
	if ok := requests.Validate(c, &request, requests.ValidateCreateOrderRequest); !ok {
		return
	}
	orderNo := GenerateOrderNo()
	// 请求支付宝接口预创建订单
	var trade = alipay.TradePreCreate{}
	trade.NotifyURL = config.Get("alipay.notify_url")
	trade.Subject = request.OrderName
	trade.OutTradeNo = orderNo
	trade.TotalAmount = strconv.FormatFloat(request.Amount, 'f', 2, 64)

	// 初始化client
	initAlipayClient()
	result, err := client.TradePreCreate(trade)
	if err != nil {
		logger.LogIf(err)
		response.Abort500(c, "创建订单失败，请稍后重试~")
		return
	}
	if result.IsFailure() {
		logger.LogIf(fmt.Errorf("code: %s - msg: %s, subCode: %s - subMsg: %s", result.Code, result.Msg, result.SubCode, result.SubMsg))
		response.Abort500(c, "创建订单失败,请稍后重试~")
		return
	}

	// 验证成功，创建数据
	orderModel := order.Order{
		OrderNo:   orderNo,
		OrderName: request.OrderName,
		Amount:    request.Amount,
		Qrcode:    result.QRCode,
		Source:    request.Source,
		Status:    "INIT", // INIT(初始化),WAIT_BUYER_PAY（交易创建，等待买家付款）、TRADE_CLOSED（未付款交易超时关闭，或支付完成后全额退款）、TRADE_SUCCESS（交易支付成功）、TRADE_FINISHED（交易结束，不可退款）
		IsPaid:    false,
		IsDeleted: false,
	}
	err = orderModel.Create()

	if err == nil {
		response.Data(c, orderModel)
	} else {
		logger.LogIf(fmt.Errorf("error occurred while inserting orderModel record for orderNo %s: %s", orderNo, err.Error()))
		response.Abort500(c, "创建订单失败，请稍后重试~")
	}
}

// Status 查询订单状态
func (oct *OrderController) Status(c *gin.Context) {
	// 验证表单
	orderNo := c.Param("order_no")
	reg := regexp.MustCompile(`^\d{23}$`)
	if found := reg.MatchString(orderNo); !found {
		response.BadRequest(c, errors.New("invalid order_no"))
		return
	}
	// 验证成功，查询数据
	orderModel := order.GetByOrderNo(orderNo)
	if orderModel.ID > 0 {
		response.Data(c, orderModel)
	} else {
		logger.Error("Order Not Found", zap.String("order_not_exist", orderModel.OrderNo))
		response.Abort404(c, fmt.Sprintf("OrderNo %s not found", orderModel.OrderNo))
	}
}

// Index 所有订单
func (oct *OrderController) Index(c *gin.Context) {
	data, pager := order.Paginate(c, 10)
	response.JSON(c, gin.H{
		"data":  data,
		"pager": pager,
	})
}
